Getting set up
Start an RStudio session at OSC
Show instructions
- Your job should start running pretty soon, and when it’s ready the box should look like this:
- Click
Connect to RStudio Server at the bottom of the box, and an RStudio Server instance will open. You’re ready to go!
Create an RStudio Project
Show instructions
basedir <- "/fs/project/PAS0471/teach/misc/2021-02_rnaseq/"
# Get your user name (by running a shell command via the `system()` function:
me <- system("echo $USER", intern = TRUE)
# Build the path to the target dir:
proj_dir <- file.path(basedir, me)
# Create the Project:
library(usethis)
create_project(path = proj_dir)
Now, RStudio will reload with the newly created Project open.
If you get the pop-up below, click Don't Save (do this whenever you get that pop-up):
Load the necessary packages
if(! "pacman" %in% installed.packages()) install.packages("pacman")
packages <- c("DESeq2", # Differential expression analysis
"tidyverse", # Misc. data manipulation and plotting
"here", # Managing file paths
"pheatmap", # Heatmap plot
"apeglm") # For LFC shrinkage
pacman::p_load(char = packages)
theme_set(theme_bw()) # Set ggplot theme
Prepare the data
Change the column names, which are very long:
colnames(raw_counts)[7:8]
## [1] "X.fs.scratch.PAS1548.JamesSeq.CAPS.tomato.star_out.X6465_Benitez.PonceM_C6_myb2_V1N_1_S1_L001_R1_001.fastq.gzAligned.sortedByCoord.out.bam"
## [2] "X.fs.scratch.PAS1548.JamesSeq.CAPS.tomato.star_out.X6465_Benitez.PonceM_C6_myb2_V1N_1_S1_L002_R1_001.fastq.gzAligned.sortedByCoord.out.bam"
my_regex <- ".+PonceM_(.+)_V1N.+"
colnames(raw_counts) <- sub(my_regex, "\\1", colnames(raw_counts))
colnames(raw_counts)
## [1] "Geneid" "Chr" "Start" "End" "Strand" "Length" "C6_myb2"
## [8] "C6_myb2" "C6_myb3" "C6_myb3" "C6_myb4" "C6_myb4" "CT_05" "CT_05"
## [15] "CT_06" "CT_06" "CT_09" "CT_09" "T_26" "T_26" "T_29"
## [22] "T_29" "T_30" "T_30" "cnt_1" "cnt_1" "cnt_2" "cnt_2"
## [29] "cnt_3" "cnt_3" "myb_1" "myb_1" "myb_2" "myb_2" "myb_3"
## [36] "myb_3"
Besides the counts, there are columns with metadata for each gene:
## Geneid Chr Start End Strand Length C6_myb2 C6_myb2.1
## 1 Solyc00g160260.1 SL4.0ch00 83863 84177 + 315 0 0
## 2 Solyc00g160270.1 SL4.0ch00 166754 167268 - 515 0 0
## 3 Solyc00g500003.1 SL4.0ch00 311496 382066 - 70571 1 4
## 4 Solyc00g500004.1 SL4.0ch00 417592 418482 + 891 0 0
## 5 Solyc00g500005.1 SL4.0ch00 478389 478640 + 252 0 0
Let’s remove those:
counts <- raw_counts[, 7:ncol(raw_counts)]
rownames(counts) <- raw_counts$Geneid
Replicate samples
In this table, there are two separate entries for each sample: each library was sequenced on two lanes. Recall that in our workflow, we had merged these FASTQ files prior to mapping, but here we are using the table based on the full dataset produced by Matthew.
So, we will go ahead and merge these replicates now, by simply summing the counts:
counts.t <- t(counts)
rownames(counts.t) <- names(raw_counts)[7:36]
sums <- rowsum(counts.t, group = rownames(counts.t))
counts <- t(sums)
(Alternatively, one could use the specialized function DESeq2::collapseReplicates() for this.)
Check sample IDs
For differential expression analysis, we will be using the popularDESeq2 R/Bioconductor package (paper, website).
We will load both the count table and the metadata into DESeq2. When doing so, DESeq2 assumes that sample IDs in both tables match and are provided in the same order. Let’s make sure this is indeed the case.
Sort both data frames alphabetically:
metadata <- metadata[order(metadata$SampleID), ]
counts <- counts[, order(colnames(counts))]
Check if names are the same:
## [1] "C6_myb2" "C6_myb3" "C6_myb4" "cnt_1" "cnt_2" "cnt_3" "CT_05"
## [8] "CT_06" "CT_09" "myb_1" "myb_2" "myb_3" "T_26" "T_29"
## [15] "T_30"
## [1] "C6_myb2" "C6_myb3" "C6_myb4" "cnt_1" "cnt_2" "cnt_3" "CT_05"
## [8] "CT_06" "CT_09" "myb_1" "myb_2" "myb_3" "T_26" "T_29"
## [15] "T_30"
matching_names <- identical(metadata$SampleID, colnames(counts))
matching_names
## [1] TRUE
if(matching_names == FALSE) stop("Sample ID in metadata and count matrix do not match!")
Create the DESeq2 object
We will create the DESeq2 object using the function DESeqDataSetFromMatrix(), which we will provide with three pieces of information:
- The count data with argument
countData.
- The metadata with argument
colData.
- The model design for the DE analysis – argument
design.
For now, we will specify ~1, which means “no design” – we will change this before the actual DE analysis.
dds_raw <- DESeqDataSetFromMatrix(countData = counts,
colData = metadata,
design = ~ 1)
Remove the Ri_noexp group
#dds_raw <- dds_raw[, -which(dds_raw@colData$group == "Ri_noexp")]
#dds_raw@colData$group <- droplevels(dds_raw@colData$group)
dds_raw <- dds_raw[, -which(dds_raw@colData$Treatment == "noexp")]
#dds_raw@colData$group <- droplevels(dds_raw@colData$group)
Explore the count data
What are number of rows and columns of the count matrix?
## [1] 34688 15
How many genes have non-zero counts?
dim(counts[rowSums(counts) > 0, ])
## [1] 28145 15
How many genes have total counts of at least 10?
dim(counts[rowSums(counts) >= 10, ])
## [1] 24771 15
Histogram of gene counts
Let’s plot a histogram of gene counts:
theme_set(theme_bw())
summed_gene_counts <- data.frame(gene_count = rowSums(counts)) %>%
rownames_to_column("gene_id")
ggplot(data = summed_gene_counts) +
geom_histogram(aes(x = gene_count), binwidth = 10000) +
scale_y_log10(expand = c(0, 0)) +
scale_x_continuous(expand = c(0,0))

Zoom in a bit:
ggplot(data = summed_gene_counts) +
geom_histogram(aes(x = gene_count), binwidth = 1000) +
scale_y_log10(expand = c(0, 0)) +
scale_x_continuous(limits = c(0, 200000), expand = c(0,0)) +
theme(plot.margin = margin(0.5, 0.7, 0.5, 0.5, "cm"))

How are counts distributed across samples? That is, we would like a sum of counts for each column. To get this, we use the apply() function, which can apply a function (in our case sum()) to all columns (hence MARGIN = 2 – for rows, use 1) of our counts dataframe:
apply(X = counts, MARGIN = 2, FUN = sum)
## C6_myb2 C6_myb3 C6_myb4 cnt_1 cnt_2 cnt_3 CT_05 CT_06
## 18122406 21913320 13659702 18844494 18890045 16634169 22183005 20620412
## CT_09 myb_1 myb_2 myb_3 T_26 T_29 T_30
## 22924853 22070790 22829207 23195471 19902716 23221528 15793928
Principal Component Analysis (PCA)
Run the PCA and prepare for plotting
First, we normalize the count data to have even sampling across samples (with respect to library size) and approximately even variance:
vsd <- varianceStabilizingTransformation(dds_raw, blind = TRUE)
Next, we run the PCA and retrieve the data to plot with ggplot2:
pcaData <- plotPCA(vsd,
ntop = 500,
intgroup = c("AMF", "Treatment"),
returnData = TRUE)
We extract the percentage of variance explained by different principal components, so we can later add this information to the plot:
percentVar <- round(100 * attr(pcaData, "percentVar"))
percentVar
## [1] 58 16
We create a plot title with the species name in italic using the somewhat bizarre expression() function:
plot_title <- expression("PCA of " * italic(Glycine ~ max) * " transcript count data")
Plot the PCA results
ggplot(pcaData,
aes(x = PC1, y = PC2, color = AMF, shape = Treatment)) +
geom_point(size = 6) +
xlab(paste0("PC1: ", percentVar[1], "% of variance")) +
ylab(paste0("PC2: ", percentVar[2], "% of variance")) +
ggtitle(plot_title)

Plot again – with sample names
library(ggrepel)
pca_plot2 <- ggplot(pcaData,
aes(PC1, PC2, color = AMF, shape = Treatment)) +
geom_point(size = 3) +
geom_label_repel(aes(label = name)) +
xlab(paste0("PC1: ", percentVar[1], "% of variance")) +
ylab(paste0("PC2: ", percentVar[2], "% of variance")) +
ggtitle(plot_title)
print(pca_plot2)

DE analysis – full dataset
The design has two factors: AMF and Treatment. Rather than fit a multivariate model, we can start by merging the two into a single factor called group, and fit a univariate model with this factor.
dds_raw$group <- factor(paste(dds_raw$AMF, dds_raw$Treatment, sep = "_"))
table(dds_raw$group)
##
## control_mock control_myb Ri_mock Ri_myb
## 3 3 3 3
We will set the “reference” level of the factor to be the double negative control (empty substrate, no Agrobacteria):
dds_raw$group <- relevel(dds_raw$group, ref = "control_mock")
dds_raw$group
## [1] control_mock control_mock control_mock Ri_mock Ri_mock
## [6] Ri_mock control_myb control_myb control_myb Ri_myb
## [11] Ri_myb Ri_myb
## Levels: control_mock control_myb Ri_mock Ri_myb
Next, we set the analysis design:
design(dds_raw) <- formula(~ group)
And finally, we perform the differential expression analysis with the DEseq() function:
The DESeq() function above performs three steps consecutively:
estimateSizeFactors() – “Normalization” by library size and composition.
Note that DESeq2 doesn’t actually normalize the counts in the sense that it produces a matrix with adjusted counts. Instead it uses raw counts and includes the size factors in the modeling.
To learn more about gene count normalization, see this video and this page.
estimateDispersions() – Estimate gene-wise dispersion (variance in counts).
nbinomWaldTest(ddsObj) – Fit the negative binomial GLM and calculate Wald statistics, which is the test statistic underlying the p-value for whether a gene is differentially expressed.
These functions could also be called separately, which would be useful if you want to be able to change more defaults.
The results table
## log2 fold change (MLE): group Ri myb vs control mock
## Wald test p-value: group Ri myb vs control mock
## DataFrame with 34688 rows and 6 columns
## baseMean log2FoldChange lfcSE stat pvalue
## <numeric> <numeric> <numeric> <numeric> <numeric>
## Solyc00g160260.1 0.0000 NA NA NA NA
## Solyc00g160270.1 0.0000 NA NA NA NA
## Solyc00g500003.1 11.1187 -0.332313 1.21815 -0.272802 0.785006
## Solyc00g500004.1 0.0000 NA NA NA NA
## Solyc00g500005.1 0.0000 NA NA NA NA
## ... ... ... ... ... ...
## Solyc12g100330.2 1165.58 0.832223 0.291271 2.857214 0.00427377
## Solyc12g100340.1 0.00 NA NA NA NA
## Solyc12g100360.1 1689.10 0.199263 0.337208 0.590921 0.55457353
## Solyc12g100363.1 0.00 NA NA NA NA
## Solyc12g100367.1 0.00 NA NA NA NA
## padj
## <numeric>
## Solyc00g160260.1 NA
## Solyc00g160270.1 NA
## Solyc00g500003.1 0.876499
## Solyc00g500004.1 NA
## Solyc00g500005.1 NA
## ... ...
## Solyc12g100330.2 0.0303467
## Solyc12g100340.1 NA
## Solyc12g100360.1 0.7152218
## Solyc12g100363.1 NA
## Solyc12g100367.1 NA
By default, the results table prints statistics comparing the last level of the factor with the first level: that is, log-fold change and p-values describe differences between these two levels specifically. However, we can easily extract equivalent statistics for any pairwise comparison among our factor levels, which we will see later.
For now, we will explore what each column in this table means:
The baseMean column contains the mean expression level across all samples.
The log2FoldChange column contains the log2-fold change of gene counts between the compared levels, that is, it represents the effect size.
A log2-fold change of 1 indicates that the expression in the reference level is two-fold lower than that of the other level, a log2-fold change of 2 indicates a four-fold difference, a log2-fold change of 3 indicates an eight-fold difference, and so on.
Similarly, negative log2-fold values indicate a change in gene counts in the other direction: the reference level is higher than the other level.
The lfcSE column indicates the uncertainty in terms of the standard error (SE) of the log2-fold change estimate.
The stat column indicates the value for the Wald test’s test statistic.
The pvalue column reported the uncorrected p-value from the Wald test.
Because we are testing significance for many genes, we need to correct for multiple testing. DESeq2 uses the Benjamini-Hochberg False Discovery Rate (FDR) correction, and these values are reported in the column padj (i.e., adjusted p-value).
A summary of this information about each column can be seen by running the mcols() function:
## DataFrame with 6 rows and 2 columns
## type description
## <character> <character>
## baseMean intermediate mean of normalized c..
## log2FoldChange results log2 fold change (ML..
## lfcSE results standard error: grou..
## stat results Wald statistic: grou..
## pvalue results Wald test p-value: g..
## padj results BH adjusted p-values
NA values in the results table
Some values in the results table can be set to NA for one of the following reasons:
If a gene contains a sample with a count outlier, both the p-value and adjusted p-value will be set to NA. (DESeq2 performs outlier detection using Cook’s distance.)
If all samples have zero counts for a given gene, the baseMean column will be zero, and the log2-fold change estimates, p-value and adjusted p-value will all be set to NA.
DESeq2 also automatically filters genes with a low mean count in the sense that it does not include them in the multiple testing correction. Therefore, in such cases, the p-value will not be NA, but the adjusted p-value will be.
Because we have very low power to detect differential expression for such low-count genes, it is beneficial to remove them prior to the multiple testing correction: that way, the correction becomes less severe for the remaining genes.
Let’s see how many genes have NA p-values:
# Number of genes with NA p-value:
sum(is.na(res$pvalue))
## [1] 6990
# As a proportion of the total number of genes in the test:
sum(is.na(res$pvalue)) / nrow(res)
## [1] 0.2015106
And NA adjusted p-values:
# Number of genes with NA p-value:
sum(is.na(res$padj))
## [1] 10709
# As a proportion of the total number of genes in the test:
sum(is.na(res$padj)) / nrow(res)
## [1] 0.3087235
DE analysis – contrast two custom groups
Using the resultsNames function, we can see which pairwise contrasts between different levels of the factor are available (though it is not displayed in a particularly readable fashion):
## [1] "Intercept" "group_control_myb_vs_control_mock"
## [3] "group_Ri_mock_vs_control_mock" "group_Ri_myb_vs_control_mock"
Not all pairwise contrasts between the 5 levels in our group factor are available here: instead, control_mock, which we set as the reference level, is being compared with all 4 other levels. (However, we can still compare other levels too.)
Above, we looked at the results (p-values and so on) for the last of these comparisons, which is what DESeq2 will show by default. To see the results table for one of the other 3 comparisons, we pass a vector to the contrast argument of the results() function with the factor (group) and the two levels to be contrasted:
my_contrast <- c("Ri_mock", "control_mock")
res <- results(dds,
contrast = c("group", my_contrast))
How many adjusted p-values were less than 0.1?
sum(res$padj < 0.1, na.rm = TRUE)
## [1] 5217
We’ll also create an object with adjusted (shrunken) LFC estimates, which will be useful for visualization and ranking of genes by LFC:
my_coef <- paste0("group_", paste0(my_contrast, collapse = "_vs_"))
my_coef
## [1] "group_Ri_mock_vs_control_mock"
res_LFC <- lfcShrink(dds, coef = my_coef, type = "apeglm", lfcThreshold = 1)
Note that here, we had to provide the contrast (“coefficient”) in the format given by resultsNames(dds) (that looks a bit confusing because this format uses underscores to separate levels, while our factor levels themselves also contain underscores).
Exploring the results
MA-plot:
For a nice overview of the results, we can plot a so-called “MA plot”. An MA plot shows, for each gene: - Count differences in terms of LFC between two groups, on the y-axis - Mean counts across both groups, on the x-axis.
We can create an MA plot using DESeq2’s plotMA function, with significantly differentially expressed genes displayed in blue:
plotMA(res, ylim = c(-5, 5))

To be able to customize the plot, we’ll use returnData = TRUE like we have done with previous plots, and then plot the resulting dataframe with ggplot2:
d <- plotMA(res, returnData = TRUE)
ggplot(d, aes(x = mean, y = lfc, color = isDE)) +
geom_point(size = 0.5) +
scale_x_log10() +
scale_y_continuous(limits = c(-10, 10)) +
scale_color_manual(values = c('grey50', 'blue')) +
guides(color = FALSE) +
labs(x = "Mean of normalized counts",
y = "LFC")

We can see that lowly-expressed genes tend to deviate from an LFC of 0 (same mean expression levels in the two groups) much more than highly-expressed genes do. However, this is an artifact of noise overpowering the signal when expression values are low. We can also see that no genes in the far left part of the plot are differentially expressed: this is due to this same lack of power.
DESeq2 provides several methods to adjust LFC estimates for this low-expression bias. We used one of those (lfcShrink()) above to produce the res_LFC object. Let’s create another MA plot with these adjusted LFC estimates:
d <- plotMA(res_LFC, ylim = c(-5, 5), returnData = TRUE)
ggplot(d, aes(x = mean, y = lfc, color = isDE)) +
geom_point(size = 0.5) +
scale_x_log10() +
scale_color_manual(values = c('grey50', 'blue')) +
guides(color = FALSE) +
labs(x = "Mean of normalized counts",
y = "Shrunken LFC")

Finally, for a plot like this it could be useful to be able to identify individual genes. There are way too many to print the gene names on the plot, though. Instead, we can also make the plot interactive with Plotly, so we can see the identity of each gene when we hover over the point:
library(plotly)
# To show the gene name, we need to have a column with gene names.
# Currently, the gene names are row names but ggplot2 (and other tidyverse
# applications) don't like that, so we create a column with gene names:
d$gene <- rownames(d)
# First we create a very similar ggplot to what we did above,
# but we assign gene names to "text":
p_ma <- ggplot(d,
aes(x = mean, y = lfc, color = isDE, text = gene)) +
geom_point(size = 0.5) +
scale_x_log10() +
scale_color_manual(values = c('grey50', 'blue')) +
guides(color = FALSE) +
labs(x = "Mean of normalized counts",
y = "Shrunken LFC")
# Finally, we make the plot interactive and tell Plotly that it should show
# the "text" (i.e. gene names) as the "tooltip", meaning upon hovering:
ggplotly(p_ma, tooltip = "text")
Plot specific genes
We can also create plot of expression levels for individual genes. That is especially interesting for genes with highly significant differential expression.
Let’s plot the top-5 most significantly differentially expressed genes:
# First, we select the 5 genes with the lowest adjusted p-value:
top5 <- row.names(res[order(res$padj), ][1:5, ])
# Then we loop over these genes, creating a plot for each one:
for(fgene in top5) {
d <- plotCounts(dds,
gene = fgene,
intgroup = "group",
returnData = TRUE)
p <- ggplot(d, aes(x = group, y = count)) +
geom_point(position = position_jitter(w = 0.1, h = 0)) +
labs(title = fgene) +
theme_bw()
print(p)
}





Heatmap
We can create heatmaps with the pheatmap function – for instance, for the 20 most highly expressed genes:
ntd <- normTransform(dds)
selection <- order(rowMeans(counts(dds, normalized = TRUE)),
decreasing = TRUE)[1:20]
ntd_sel <- assay(ntd)[selection, ]
df_meta <- as.data.frame(colData(dds)[, c("AMF", "Treatment")])
pheatmap(ntd_sel,
cluster_rows = FALSE,
cluster_cols = FALSE,
show_rownames = FALSE,
annotation_col = df_meta)

Export the results
Let’s save the results dataframe to file.
Note that it will only contain the results for one comparison. Also, if we write the results dataframe to file, we won’t be able to tell from the file what the comparison is, so let’s store that in two columns:
## [1] "Ri_mock" "control_mock"
my_contrast_pasted <- paste0(my_contrast, collapse = "_vs_")
my_contrast_pasted
## [1] "Ri_mock_vs_control_mock"
res$level1 <- my_contrast[1]
res$level2 <- my_contrast[2]
head(res)
## log2 fold change (MLE): group Ri_mock vs control_mock
## Wald test p-value: group Ri mock vs control mock
## DataFrame with 6 rows and 8 columns
## baseMean log2FoldChange lfcSE stat pvalue
## <numeric> <numeric> <numeric> <numeric> <numeric>
## Solyc00g160260.1 0.0000 NA NA NA NA
## Solyc00g160270.1 0.0000 NA NA NA NA
## Solyc00g500003.1 11.1187 -1.91798 1.25512 -1.52813 0.12648
## Solyc00g500004.1 0.0000 NA NA NA NA
## Solyc00g500005.1 0.0000 NA NA NA NA
## Solyc00g500006.1 0.0000 NA NA NA NA
## padj level1 level2
## <numeric> <character> <character>
## Solyc00g160260.1 NA Ri_mock control_mock
## Solyc00g160270.1 NA Ri_mock control_mock
## Solyc00g500003.1 0.304046 Ri_mock control_mock
## Solyc00g500004.1 NA Ri_mock control_mock
## Solyc00g500005.1 NA Ri_mock control_mock
## Solyc00g500006.1 NA Ri_mock control_mock
Now we can write res to file:
res_file <- file.path(outdir, paste0(my_contrast_pasted, '_all-res.txt'))
write.table(res, res_file,
sep = '\t', row.names = TRUE, quote = FALSE)
We may also want to save a separate table with only significant results:
res_sig_file <- file.path(outdir, paste0(my_contrast_pasted, '_sig-res.txt'))
res_sig <- res[res$padj < 0.1, ]
write.table(res_sig, res_sig_file,
sep = '\t', row.names = TRUE, quote = FALSE)
DE analysis – all pairwise comparisons
TBA
DE analysis – with two factors
TBA
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBhbmFseXNpcyA8YnI+IHdpdGggREVTZXEyIgphdXRob3I6ICJKZWxtZXIgUG9lbHN0cmEiCmRhdGU6ICIzLzI2LzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBhbmNob3Jfc2VjdGlvbnM6IHRydWUKICAgIGNzczogaHRtbF9wYWdlLmNzcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobz1UUlVFLCBldmFsPVRSVUUsIGNhY2hlPVRSVUUsCiAgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwKICBjbGFzcy5zb3VyY2UgPSAicl9jb2RlIiwKICBjbGFzcy5vdXRwdXQgPSAicl9vdXRwdXQiLAogIGNsYXNzLndhcm5pbmcgPSAicl93YXJuaW5nIiwKICBjbGFzcy5tZXNzYWdlID0gInJfd2FybmluZyIsCiAgY2xhc3MuZXJyb3IgPSAicl9lcnJvciIKICApCmBgYAoKPGJyPiA8YnI+CgotLS0tCgojIyBHZXR0aW5nIHNldCB1cAoKIyMjIFN0YXJ0IGFuIFJTdHVkaW8gc2Vzc2lvbiBhdCBPU0MKCjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgaW5zdHJ1Y3Rpb25zPC9zdW1tYXJ5PgoKLSBMb2dpbiB0byBPU0MgYXQgPGh0dHBzOi8vb25kZW1hbmQub3NjLmVkdT4uCgotIENsaWNrIG9uIGBJbnRlcmFjdGl2ZSBBcHBzYCAodG9wIGJhcikgPiBgUlN0dWRpbyBTZXJ2ZXIgKE93ZW5zIGFuZCBQaXR6ZXIpYAoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9hcHBzLnBuZyB3aWR0aD0iNTAwIj4KPC9wPgoKLSBGaWxsIG91dCB0aGUgZm9ybSBhcyBzaG93biBbaGVyZV0oc2xpZGVzLzAzLU9TQy1zbGlkZXMuaHRtbCNyc3R1ZGlvX3NlcnZlcl9qb2IpLgoKLSBOb3csIHlvdSBzaG91bGQgc2VlIGEgYm94IGxpa2UgdGhpczoKCjxwIGFsaWduPSJjZW50ZXIiPgo8aW1nIHNyYz1pbWcvb3NjX3F1ZXVlZC5wbmcgd2lkdGg9IjcwMCI+CjwvcD4KCi0gWW91ciBqb2Igc2hvdWxkIHN0YXJ0IHJ1bm5pbmcgcHJldHR5IHNvb24sIGFuZCB3aGVuIGl0J3MgcmVhZHkgdGhlIGJveCBzaG91bGQgbG9vayBsaWtlIHRoaXM6IAoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9vc2NfcnVubmluZy5wbmcgd2lkdGg9IjcwMCI+CjwvcD4KCi0gQ2xpY2sgYENvbm5lY3QgdG8gUlN0dWRpbyBTZXJ2ZXJgIGF0IHRoZSBib3R0b20gb2YgdGhlIGJveCwgYW5kIGFuIFJTdHVkaW8gU2VydmVyIGluc3RhbmNlIHdpbGwgb3Blbi4gWW91J3JlIHJlYWR5IHRvIGdvIQoKPC9kZXRhaWxzPgoKIyMjIENyZWF0ZSBhbiBSU3R1ZGlvIFByb2plY3QKCjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgaW5zdHJ1Y3Rpb25zPC9zdW1tYXJ5PgoKYGBge3IsIGV2YWw9RkFMU0V9CmJhc2VkaXIgPC0gIi9mcy9wcm9qZWN0L1BBUzA0NzEvdGVhY2gvbWlzYy8yMDIxLTAyX3JuYXNlcS8iCgojIEdldCB5b3VyIHVzZXIgbmFtZSAoYnkgcnVubmluZyBhIHNoZWxsIGNvbW1hbmQgdmlhIHRoZSBgc3lzdGVtKClgIGZ1bmN0aW9uOgptZSA8LSBzeXN0ZW0oImVjaG8gJFVTRVIiLCBpbnRlcm4gPSBUUlVFKQoKIyBCdWlsZCB0aGUgcGF0aCB0byB0aGUgdGFyZ2V0IGRpcjoKcHJval9kaXIgPC0gZmlsZS5wYXRoKGJhc2VkaXIsIG1lKQoKIyBDcmVhdGUgdGhlIFByb2plY3Q6CmxpYnJhcnkodXNldGhpcykKY3JlYXRlX3Byb2plY3QocGF0aCA9IHByb2pfZGlyKQpgYGAKCk5vdywgUlN0dWRpbyB3aWxsIHJlbG9hZCB3aXRoIHRoZSBuZXdseSBjcmVhdGVkIFByb2plY3Qgb3Blbi4KCklmIHlvdSBnZXQgdGhlIHBvcC11cCBiZWxvdywgY2xpY2sgYERvbid0IFNhdmVgIChkbyB0aGlzIHdoZW5ldmVyIHlvdSBnZXQgdGhhdCBwb3AtdXApOgoKPHAgYWxpZ249ImNlbnRlciI+CjxpbWcgc3JjPWltZy9yZGF0YS1wb3B1cC5wbmcgd2lkdGg9IjM1MCI+CjwvcD4KCjwvZGV0YWlscz4KCiMjIyBDb3B5IHRoZSBjb3VudCBkYXRhIGFuZCB0aGUgbWV0YWRhdGEgZmlsZXMKCkdvIHRoZSBgVGVybWluYWxgIHRhYiBuZXh0IHRvIHRoZSBSIGBDb25zb2xlYCBpbiB0aGUgYm90dG9tIGxlZnQgb2YgdGhlIFJTdHVkaW8Kd2luZG93OgoKYGBge3NoLCBldmFsPUZBTFNFfQpjZCAvZnMvcHJvamVjdC9QQVMwNDcxL3RlYWNoL21pc2MvMjAyMS0wMl9ybmFzZXEvJFVTRVIKCmNwIC4uL21hc3Rlci9kYXRhL21ldGEvbWV0YWRhdGEudHh0IGRhdGEvbWV0YQpjcCAuLi9tYXN0ZXIvcmVzdWx0cy9jb3VudC9nZW5lX2NvdW50c19hbGwudHh0IHJlc3VsdHMvY291bnQKYGBgCgojIyMgTG9hZCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KaWYoISAicGFjbWFuIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQoKcGFja2FnZXMgPC0gYygiREVTZXEyIiwgICAgICAgICAgIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwogICAgICAgICAgICAgICJ0aWR5dmVyc2UiLCAgICAgICAjIE1pc2MuIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBwbG90dGluZwogICAgICAgICAgICAgICJoZXJlIiwgICAgICAgICAgICAjIE1hbmFnaW5nIGZpbGUgcGF0aHMKICAgICAgICAgICAgICAicGhlYXRtYXAiLCAgICAgICAgIyBIZWF0bWFwIHBsb3QKICAgICAgICAgICAgICAiYXBlZ2xtIikgICAgICAgICAgIyBGb3IgTEZDIHNocmlua2FnZQpwYWNtYW46OnBfbG9hZChjaGFyID0gcGFja2FnZXMpCgp0aGVtZV9zZXQodGhlbWVfYncoKSkgICMgU2V0IGdncGxvdCB0aGVtZQpgYGAKCiMjIyBJbnB1dCBhbmQgb3V0cHV0IGRpcnMgYW5kIGZpbGVzCgpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpjb3VudF90YWJsZV9maWxlIDwtICJyZXN1bHRzL2NvdW50L2dlbmVfY291bnRzX2FsbC50eHQiCm1ldGFkYXRhX2ZpbGUgPC0gImRhdGEvbWV0YS9tZXRhZGF0YS50eHQiCgpvdXRkaXIgPC0gInJlc3VsdHMvREUvIgpwbG90ZGlyIDwtICJyZXN1bHRzL0RFL2ZpZy8iCmBgYAoKSW5wdXQgZmlsZXM6CgotICoqR2VuZSBjb3VudHMgdGFibGUqKiAtLSB0aGUgZmlsZSBleGFjdGx5IGFzIGl0IHdhcyB3cml0dGVuIGJ5ICpmZWF0dXJlQ291bnRzKi4KICAKLSAqKk1ldGFkYXRhIHRhYmxlKiogLS0gc28gd2UgY2FuIGdyb3VwIG91ciBzYW1wbGVzIGFuZCBtYWtlIGNvbXBhcmlzb25zIGJldHdlZW4KICB0aGVzZSBncm91cHMuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY291bnRfdGFibGVfZmlsZSA8LSBoZXJlKCJyZXN1bHRzL2NvdW50L2dlbmVfY291bnRzX2FsbC50eHQiKQptZXRhZGF0YV9maWxlIDwtIGhlcmUoImRhdGEvbWV0YS9tZXRhZGF0YS50eHQiKQpgYGAKCk91dHB1dCBkaXJlY3RvcmllcyAtLSBhbmQgd2UgY3JlYXRlIHRoZW0gaWYgdGhleSBkb24ndCBhbHJlYWR5IGV4aXN0OgoKYGBge3J9Cm91dGRpciA8LSBoZXJlKCJyZXN1bHRzL0RFLyIpCnBsb3RkaXIgPC0gaGVyZSgicmVzdWx0cy9ERS9maWcvIikKCmlmICghZGlyLmV4aXN0cyhwbG90ZGlyKSkgZGlyLmNyZWF0ZShwbG90ZGlyLCByZWN1cnNpdmUgPSBUUlVFKQppZiAoIWRpci5leGlzdHMocGxvdGRpcikpIGRpci5jcmVhdGUocGxvdGRpciwgcmVjdXJzaXZlID0gVFJVRSkKYGBgCgojIyMgTG9hZCBpbnB1dCBkYXRhCgpMb2FkIHRoZSBjb3VudCB0YWJsZSBmcm9tICpmZWF0dXJlQ291bnRzKjoKCmBgYHtyLCBldmFsPVRSVUV9CnJhd19jb3VudHMgPC0gcmVhZC50YWJsZShjb3VudF90YWJsZV9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwgaGVhZGVyID0gVFJVRSwgc2tpcCA9IDEpCmBgYAoKTG9hZCB0aGUgbWV0YWRhdGEgaW5mb3JtYXRpb246CgpgYGB7cn0KbWV0YWRhdGEgPC0gcmVhZC50YWJsZShtZXRhZGF0YV9maWxlLCBoZWFkZXIgPSBUUlVFKQoKaGVhZChtZXRhZGF0YSkKYGBgCgpUaGUgYFRyZWF0bWVudGAgY29sdW1uIGN1cnJlbnRseSBoYXMgdGhlIHZhbHVlcyBgQWdyb2JhY3Rlcml1bV9ub2V4cGAsCmBBZ3JvYmFjdGVyaXVtX215YmAsIGFuZCBgbW9ja2AuCgpUbyBzaG9ydGVuIHRoaXMgYSBiaXQsIHdlJ2xsIGdldCByaWQgb2YgIkFncm9iYWN0ZXJpdW1fIjoKCmBgYHtyfQptZXRhZGF0YSRUcmVhdG1lbnQgPC0gc3ViKCJBZ3JvYmFjdGVyaXVtXyIsICIiLCBtZXRhZGF0YSRUcmVhdG1lbnQpCgp1bmlxdWUobWV0YWRhdGEkVHJlYXRtZW50KQpgYGAKCgo8YnI+CgotLS0tCgojIyBQcmVwYXJlIHRoZSBkYXRhCgpDaGFuZ2UgdGhlIGNvbHVtbiBuYW1lcywgd2hpY2ggYXJlIHZlcnkgbG9uZzoKCmBgYHtyfQpjb2xuYW1lcyhyYXdfY291bnRzKVs3OjhdCmBgYAoKYGBge3J9Cm15X3JlZ2V4IDwtICIuK1BvbmNlTV8oLispX1YxTi4rIgpjb2xuYW1lcyhyYXdfY291bnRzKSA8LSBzdWIobXlfcmVnZXgsICJcXDEiLCBjb2xuYW1lcyhyYXdfY291bnRzKSkKCmNvbG5hbWVzKHJhd19jb3VudHMpCmBgYAoKQmVzaWRlcyB0aGUgY291bnRzLCB0aGVyZSBhcmUgY29sdW1ucyB3aXRoIG1ldGFkYXRhIGZvciBlYWNoIGdlbmU6CgpgYGB7cn0KcmF3X2NvdW50c1sxOjUsIDE6OF0KYGBgCgpMZXQncyByZW1vdmUgdGhvc2U6CgpgYGB7cn0KY291bnRzIDwtIHJhd19jb3VudHNbLCA3Om5jb2wocmF3X2NvdW50cyldCnJvd25hbWVzKGNvdW50cykgPC0gcmF3X2NvdW50cyRHZW5laWQKYGBgCgojIyMgUmVwbGljYXRlIHNhbXBsZXMKCkluIHRoaXMgdGFibGUsIHRoZXJlIGFyZSB0d28gc2VwYXJhdGUgZW50cmllcyBmb3IgZWFjaCBzYW1wbGU6CmVhY2ggbGlicmFyeSB3YXMgc2VxdWVuY2VkIG9uIHR3byBsYW5lcy4KUmVjYWxsIHRoYXQgaW4gb3VyIHdvcmtmbG93LCB3ZSBoYWQgbWVyZ2VkIHRoZXNlIEZBU1RRIGZpbGVzIHByaW9yIHRvIG1hcHBpbmcsCmJ1dCBoZXJlIHdlIGFyZSB1c2luZyB0aGUgdGFibGUgYmFzZWQgb24gdGhlIGZ1bGwgZGF0YXNldCBwcm9kdWNlZCBieSBNYXR0aGV3LgoKU28sIHdlIHdpbGwgZ28gYWhlYWQgYW5kIG1lcmdlIHRoZXNlIHJlcGxpY2F0ZXMgbm93LCBieSBzaW1wbHkgc3VtbWluZyB0aGUgY291bnRzOgoKYGBge3J9CmNvdW50cy50IDwtIHQoY291bnRzKQpyb3duYW1lcyhjb3VudHMudCkgPC0gbmFtZXMocmF3X2NvdW50cylbNzozNl0Kc3VtcyA8LSByb3dzdW0oY291bnRzLnQsIGdyb3VwID0gcm93bmFtZXMoY291bnRzLnQpKQpjb3VudHMgPC0gdChzdW1zKQpgYGAKCihBbHRlcm5hdGl2ZWx5LCBvbmUgY291bGQgdXNlIHRoZSBzcGVjaWFsaXplZCBmdW5jdGlvbgpgREVTZXEyOjpjb2xsYXBzZVJlcGxpY2F0ZXMoKWAgZm9yIHRoaXMuKQoKIyMjIENoZWNrIHNhbXBsZSBJRHMKCkZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcywgd2Ugd2lsbCBiZSB1c2luZyB0aGUgcG9wdWxhcioqREVTZXEyKioKUi9CaW9jb25kdWN0b3IgcGFja2FnZQooW3BhcGVyXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE0LTA1NTAtOCksClt3ZWJzaXRlXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvREVTZXEyLmh0bWwpKS4KCldlIHdpbGwgbG9hZCBib3RoIHRoZSBjb3VudCB0YWJsZSBhbmQgdGhlIG1ldGFkYXRhIGludG8gKkRFU2VxMiouCldoZW4gZG9pbmcgc28sICpERVNlcTIqIGFzc3VtZXMgdGhhdCBzYW1wbGUgSURzIGluIGJvdGggdGFibGVzIG1hdGNoIGFuZCAKYXJlIHByb3ZpZGVkIGluIHRoZSBzYW1lIG9yZGVyLiBMZXQncyBtYWtlIHN1cmUgdGhpcyBpcyBpbmRlZWQgdGhlIGNhc2UuCgpTb3J0IGJvdGggZGF0YSBmcmFtZXMgYWxwaGFiZXRpY2FsbHk6CgpgYGB7cn0KbWV0YWRhdGEgPC0gbWV0YWRhdGFbb3JkZXIobWV0YWRhdGEkU2FtcGxlSUQpLCBdCmNvdW50cyA8LSBjb3VudHNbLCBvcmRlcihjb2xuYW1lcyhjb3VudHMpKV0KYGBgCgpDaGVjayBpZiBuYW1lcyBhcmUgdGhlIHNhbWU6CgpgYGB7cn0KbWV0YWRhdGEkU2FtcGxlSUQKCmNvbG5hbWVzKGNvdW50cykKCm1hdGNoaW5nX25hbWVzIDwtIGlkZW50aWNhbChtZXRhZGF0YSRTYW1wbGVJRCwgY29sbmFtZXMoY291bnRzKSkKbWF0Y2hpbmdfbmFtZXMKaWYobWF0Y2hpbmdfbmFtZXMgPT0gRkFMU0UpIHN0b3AoIlNhbXBsZSBJRCBpbiBtZXRhZGF0YSBhbmQgY291bnQgbWF0cml4IGRvIG5vdCBtYXRjaCEiKQpgYGAKCiMjIyBDcmVhdGUgdGhlIERFU2VxMiBvYmplY3QKCldlIHdpbGwgY3JlYXRlIHRoZSBERVNlcTIgb2JqZWN0IHVzaW5nIHRoZSBmdW5jdGlvbiBgREVTZXFEYXRhU2V0RnJvbU1hdHJpeCgpYCwKd2hpY2ggd2Ugd2lsbCBwcm92aWRlIHdpdGggdGhyZWUgcGllY2VzIG9mIGluZm9ybWF0aW9uOgoKLSBUaGUgY291bnQgZGF0YSB3aXRoIGFyZ3VtZW50IGBjb3VudERhdGFgLgotIFRoZSBtZXRhZGF0YSB3aXRoIGFyZ3VtZW50IGBjb2xEYXRhYC4KLSBUaGUgbW9kZWwgZGVzaWduIGZvciB0aGUgREUgYW5hbHlzaXMgLS0gYXJndW1lbnQgYGRlc2lnbmAuICAKICBGb3Igbm93LCB3ZSB3aWxsIHNwZWNpZnkgYH4xYCwgd2hpY2ggbWVhbnMgIm5vIGRlc2lnbiIgLS0KICB3ZSB3aWxsIGNoYW5nZSB0aGlzIGJlZm9yZSB0aGUgYWN0dWFsIERFIGFuYWx5c2lzLgoKYGBge3J9CmRkc19yYXcgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IDEpCmBgYAoKIyMjIFJlbW92ZSB0aGUgYFJpX25vZXhwYCBncm91cAoKYGBge3J9CiNkZHNfcmF3IDwtIGRkc19yYXdbLCAtd2hpY2goZGRzX3Jhd0Bjb2xEYXRhJGdyb3VwID09ICJSaV9ub2V4cCIpXQojZGRzX3Jhd0Bjb2xEYXRhJGdyb3VwIDwtIGRyb3BsZXZlbHMoZGRzX3Jhd0Bjb2xEYXRhJGdyb3VwKQoKZGRzX3JhdyA8LSBkZHNfcmF3WywgLXdoaWNoKGRkc19yYXdAY29sRGF0YSRUcmVhdG1lbnQgPT0gIm5vZXhwIildCiNkZHNfcmF3QGNvbERhdGEkZ3JvdXAgPC0gZHJvcGxldmVscyhkZHNfcmF3QGNvbERhdGEkZ3JvdXApCmBgYAoKPGJyPgoKLS0tLQoKIyMgRXhwbG9yZSB0aGUgY291bnQgZGF0YQoKV2hhdCBhcmUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMgb2YgdGhlIGNvdW50IG1hdHJpeD8KCmBgYHtyfQpkaW0oY291bnRzKQpgYGAKCkhvdyBtYW55IGdlbmVzIGhhdmUgbm9uLXplcm8gY291bnRzPwoKYGBge3J9CmRpbShjb3VudHNbcm93U3Vtcyhjb3VudHMpID4gMCwgXSkKCmBgYAoKSG93IG1hbnkgZ2VuZXMgaGF2ZSB0b3RhbCBjb3VudHMgb2YgYXQgbGVhc3QgMTA/CgpgYGB7cn0KZGltKGNvdW50c1tyb3dTdW1zKGNvdW50cykgPj0gMTAsIF0pCmBgYAoKIyMjIEhpc3RvZ3JhbSBvZiBnZW5lIGNvdW50cwoKTGV0J3MgcGxvdCBhIGhpc3RvZ3JhbSBvZiBnZW5lIGNvdW50czoKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCnN1bW1lZF9nZW5lX2NvdW50cyA8LSBkYXRhLmZyYW1lKGdlbmVfY291bnQgPSByb3dTdW1zKGNvdW50cykpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9pZCIpCgpnZ3Bsb3QoZGF0YSA9IHN1bW1lZF9nZW5lX2NvdW50cykgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZ2VuZV9jb3VudCksIGJpbndpZHRoID0gMTAwMDApICsKICBzY2FsZV95X2xvZzEwKGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKQpgYGAKClpvb20gaW4gYSBiaXQ6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBzdW1tZWRfZ2VuZV9jb3VudHMpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGdlbmVfY291bnQpLCBiaW53aWR0aCA9IDEwMDApICsKICBzY2FsZV95X2xvZzEwKGV4cGFuZCA9IGMoMCwgMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyMDAwMDApLCBleHBhbmQgPSBjKDAsMCkpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigwLjUsIDAuNywgMC41LCAwLjUsICJjbSIpKQpgYGAKCkhvdyBhcmUgY291bnRzIGRpc3RyaWJ1dGVkIGFjcm9zcyBzYW1wbGVzPwpUaGF0IGlzLCB3ZSB3b3VsZCBsaWtlIGEgc3VtIG9mIGNvdW50cyBmb3IgZWFjaCBjb2x1bW4uClRvIGdldCB0aGlzLCB3ZSB1c2UgdGhlIGBhcHBseSgpYCBmdW5jdGlvbiwgd2hpY2ggY2FuIGFwcGx5IGEgZnVuY3Rpb24KKGluIG91ciBjYXNlIGBzdW0oKWApIHRvIGFsbCBjb2x1bW5zIChoZW5jZSBgTUFSR0lOID0gMmAgLS0gZm9yIHJvd3MsIHVzZSBgMWApCm9mIG91ciBgY291bnRzYCBkYXRhZnJhbWU6CgpgYGB7cn0KYXBwbHkoWCA9IGNvdW50cywgTUFSR0lOID0gMiwgRlVOID0gc3VtKQpgYGAKCjxicj4KCi0tLS0KCiMjIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgCgojIyMgUnVuIHRoZSBQQ0EgYW5kIHByZXBhcmUgZm9yIHBsb3R0aW5nCgpGaXJzdCwgd2Ugbm9ybWFsaXplIHRoZSBjb3VudCBkYXRhIHRvIGhhdmUgZXZlbiBzYW1wbGluZyBhY3Jvc3Mgc2FtcGxlcwood2l0aCByZXNwZWN0IHRvIGxpYnJhcnkgc2l6ZSkgYW5kIGFwcHJveGltYXRlbHkgZXZlbiB2YXJpYW5jZToKCmBgYHtyfQp2c2QgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRkc19yYXcsIGJsaW5kID0gVFJVRSkKYGBgCgpOZXh0LCB3ZSBydW4gdGhlIFBDQSBhbmQgcmV0cmlldmUgdGhlIGRhdGEgdG8gcGxvdCB3aXRoICpnZ3Bsb3QyKjoKCmBgYHtyfQpwY2FEYXRhIDwtIHBsb3RQQ0EodnNkLAogICAgICAgICAgICAgICAgICAgbnRvcCA9IDUwMCwKICAgICAgICAgICAgICAgICAgIGludGdyb3VwID0gYygiQU1GIiwgIlRyZWF0bWVudCIpLAogICAgICAgICAgICAgICAgICAgcmV0dXJuRGF0YSA9IFRSVUUpCmBgYAoKV2UgZXh0cmFjdCB0aGUgcGVyY2VudGFnZSBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgZGlmZmVyZW50IHByaW5jaXBhbCBjb21wb25lbnRzLApzbyB3ZSBjYW4gbGF0ZXIgYWRkIHRoaXMgaW5mb3JtYXRpb24gdG8gdGhlIHBsb3Q6CgpgYGB7cn0KcGVyY2VudFZhciA8LSByb3VuZCgxMDAgKiBhdHRyKHBjYURhdGEsICJwZXJjZW50VmFyIikpCnBlcmNlbnRWYXIKYGBgCgpXZSBjcmVhdGUgYSBwbG90IHRpdGxlIHdpdGggdGhlIHNwZWNpZXMgbmFtZSBpbiBpdGFsaWMgdXNpbmcgdGhlCnNvbWV3aGF0IGJpemFycmUgYGV4cHJlc3Npb24oKWAgZnVuY3Rpb246IAoKYGBge3J9CnBsb3RfdGl0bGUgPC0gZXhwcmVzc2lvbigiUENBIG9mICIgKiBpdGFsaWMoR2x5Y2luZSB+IG1heCkgKiAiIHRyYW5zY3JpcHQgY291bnQgZGF0YSIpCmBgYAoKIyMjIFBsb3QgdGhlIFBDQSByZXN1bHRzCgpgYGB7cn0KZ2dwbG90KHBjYURhdGEsCiAgICAgICBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBBTUYsIHNoYXBlID0gVHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDYpICsKICB4bGFiKHBhc3RlMCgiUEMxOiAiLCBwZXJjZW50VmFyWzFdLCAiJSBvZiB2YXJpYW5jZSIpKSArCiAgeWxhYihwYXN0ZTAoIlBDMjogIiwgcGVyY2VudFZhclsyXSwgIiUgb2YgdmFyaWFuY2UiKSkgKwogIGdndGl0bGUocGxvdF90aXRsZSkKYGBgCgojIyMgUGxvdCBhZ2FpbiAtLSB3aXRoIHNhbXBsZSBuYW1lcwoKYGBge3J9CmxpYnJhcnkoZ2dyZXBlbCkKCnBjYV9wbG90MiA8LSBnZ3Bsb3QocGNhRGF0YSwKICAgICAgICAgICAgICBhZXMoUEMxLCBQQzIsIGNvbG9yID0gQU1GLCBzaGFwZSA9IFRyZWF0bWVudCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBuYW1lKSkgKwogIHhsYWIocGFzdGUwKCJQQzE6ICIsIHBlcmNlbnRWYXJbMV0sICIlIG9mIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLCBwZXJjZW50VmFyWzJdLCAiJSBvZiB2YXJpYW5jZSIpKSArCiAgZ2d0aXRsZShwbG90X3RpdGxlKQoKcHJpbnQocGNhX3Bsb3QyKQpgYGAKCjxicj4KCi0tLS0KCiMjIERFIGFuYWx5c2lzIC0tIGZ1bGwgZGF0YXNldAoKVGhlIGRlc2lnbiBoYXMgdHdvIGZhY3RvcnM6IGBBTUZgIGFuZCBgVHJlYXRtZW50YC4KUmF0aGVyIHRoYW4gZml0IGEgbXVsdGl2YXJpYXRlIG1vZGVsLCB3ZSBjYW4gc3RhcnQgYnkgbWVyZ2luZyB0aGUgdHdvIGludG8gYQpzaW5nbGUgZmFjdG9yIGNhbGxlZCBgZ3JvdXBgLCBhbmQgZml0IGEgdW5pdmFyaWF0ZSBtb2RlbCB3aXRoIHRoaXMgZmFjdG9yLgoKYGBge3J9CmRkc19yYXckZ3JvdXAgPC0gZmFjdG9yKHBhc3RlKGRkc19yYXckQU1GLCBkZHNfcmF3JFRyZWF0bWVudCwgc2VwID0gIl8iKSkKdGFibGUoZGRzX3JhdyRncm91cCkKYGBgCgpXZSB3aWxsIHNldCB0aGUgInJlZmVyZW5jZSIgbGV2ZWwgb2YgdGhlIGZhY3RvciB0byBiZSB0aGUgZG91YmxlIG5lZ2F0aXZlIGNvbnRyb2wKKGVtcHR5IHN1YnN0cmF0ZSwgbm8gQWdyb2JhY3RlcmlhKToKCmBgYHtyfQpkZHNfcmF3JGdyb3VwIDwtIHJlbGV2ZWwoZGRzX3JhdyRncm91cCwgcmVmID0gImNvbnRyb2xfbW9jayIpCmRkc19yYXckZ3JvdXAKYGBgCgpOZXh0LCB3ZSBzZXQgdGhlIGFuYWx5c2lzIGRlc2lnbjoKCmBgYHtyfQpkZXNpZ24oZGRzX3JhdykgPC0gZm9ybXVsYSh+IGdyb3VwKQpgYGAKCkFuZCBmaW5hbGx5LCB3ZSBwZXJmb3JtIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB3aXRoIHRoZSBgREVzZXEoKWAKZnVuY3Rpb246CgpgYGB7cn0KZGRzIDwtIERFU2VxKGRkc19yYXcpCmBgYAoKVGhlIGBERVNlcSgpYCBmdW5jdGlvbiBhYm92ZSBwZXJmb3JtcyB0aHJlZSBzdGVwcyBjb25zZWN1dGl2ZWx5OgogIAotIGBlc3RpbWF0ZVNpemVGYWN0b3JzKClgIC0tICJOb3JtYWxpemF0aW9uIiBieSBsaWJyYXJ5IHNpemUgYW5kIGNvbXBvc2l0aW9uLgogIAogIE5vdGUgdGhhdCAqREVTZXEyKiBkb2VzbuKAmXQgYWN0dWFsbHkgbm9ybWFsaXplIHRoZSBjb3VudHMgaW4gdGhlIHNlbnNlIHRoYXQKICBpdCBwcm9kdWNlcyBhIG1hdHJpeCB3aXRoIGFkanVzdGVkIGNvdW50cy4KICBJbnN0ZWFkIGl0IHVzZXMgcmF3IGNvdW50cyBhbmQgaW5jbHVkZXMgdGhlIHNpemUgZmFjdG9ycyBpbiB0aGUgbW9kZWxpbmcuCiAgCiAgVG8gbGVhcm4gbW9yZSBhYm91dCBnZW5lIGNvdW50IG5vcm1hbGl6YXRpb24sIHNlZQogIFt0aGlzIHZpZGVvXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PVVGQjk5M3h1ZlVVKSBhbmQKICBbdGhpcyBwYWdlXShodHRwczovL2hiY3RyYWluaW5nLmdpdGh1Yi5pby9ER0Vfd29ya3Nob3AvbGVzc29ucy8wMl9ER0VfY291bnRfbm9ybWFsaXphdGlvbi5odG1sKS4KICAKLSBgZXN0aW1hdGVEaXNwZXJzaW9ucygpYCAtLSBFc3RpbWF0ZSBnZW5lLXdpc2UgZGlzcGVyc2lvbiAodmFyaWFuY2UgaW4gY291bnRzKS4KICAKLSBgbmJpbm9tV2FsZFRlc3QoZGRzT2JqKWAgLS0gRml0IHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBHTE0gYW5kIGNhbGN1bGF0ZQogIFdhbGQgc3RhdGlzdGljcywgd2hpY2ggaXMgdGhlIHRlc3Qgc3RhdGlzdGljIHVuZGVybHlpbmcgdGhlIHAtdmFsdWUKICBmb3Igd2hldGhlciBhIGdlbmUgaXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLgoKVGhlc2UgZnVuY3Rpb25zIGNvdWxkIGFsc28gYmUgY2FsbGVkIHNlcGFyYXRlbHksCndoaWNoIHdvdWxkIGJlIHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBiZSBhYmxlIHRvIGNoYW5nZSBtb3JlIGRlZmF1bHRzLgoKIyMjIFRoZSByZXN1bHRzIHRhYmxlCgpgYGB7cn0KcmVzIDwtIHJlc3VsdHMoZGRzKQpyZXMKYGBgCgpCeSBkZWZhdWx0LCB0aGUgcmVzdWx0cyB0YWJsZSBwcmludHMgc3RhdGlzdGljcwoqKmNvbXBhcmluZyB0aGUgbGFzdCBsZXZlbCBvZiB0aGUgZmFjdG9yIHdpdGggdGhlIGZpcnN0IGxldmVsKio6CnRoYXQgaXMsIGxvZy1mb2xkIGNoYW5nZSBhbmQgcC12YWx1ZXMgZGVzY3JpYmUKZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGVzZSB0d28gbGV2ZWxzIHNwZWNpZmljYWxseS4KSG93ZXZlciwgd2UgY2FuIGVhc2lseSBleHRyYWN0IGVxdWl2YWxlbnQgc3RhdGlzdGljcyBmb3IgYW55IHBhaXJ3aXNlIGNvbXBhcmlzb24KYW1vbmcgb3VyIGZhY3RvciBsZXZlbHMsIHdoaWNoIHdlIHdpbGwgc2VlIGxhdGVyLgoKRm9yIG5vdywgd2Ugd2lsbCBleHBsb3JlIHdoYXQgZWFjaCBjb2x1bW4gaW4gdGhpcyB0YWJsZSBtZWFuczoKCi0gVGhlICoqYGJhc2VNZWFuYCoqIGNvbHVtbiBjb250YWlucyB0aGUgbWVhbiBleHByZXNzaW9uIGxldmVsIGFjcm9zcyBhbGwgc2FtcGxlcy4KCi0gVGhlICoqYGxvZzJGb2xkQ2hhbmdlYCoqIGNvbHVtbiBjb250YWlucyB0aGUgbG9nMi1mb2xkIGNoYW5nZSBvZiBnZW5lIGNvdW50cwogIGJldHdlZW4gdGhlIGNvbXBhcmVkIGxldmVscywgdGhhdCBpcywgaXQgcmVwcmVzZW50cyB0aGUgKmVmZmVjdCBzaXplKi4KICAKICBBIGxvZzItZm9sZCBjaGFuZ2Ugb2YgMSBpbmRpY2F0ZXMgdGhhdCB0aGUgZXhwcmVzc2lvbiBpbiB0aGUgcmVmZXJlbmNlIGxldmVsCiAgaXMgdHdvLWZvbGQgKmxvd2VyKiB0aGFuIHRoYXQgb2YgdGhlIG90aGVyIGxldmVsLAogIGEgbG9nMi1mb2xkIGNoYW5nZSBvZiAyIGluZGljYXRlcyBhIGZvdXItZm9sZCBkaWZmZXJlbmNlLAogIGEgbG9nMi1mb2xkIGNoYW5nZSBvZiAzIGluZGljYXRlcyBhbiBlaWdodC1mb2xkIGRpZmZlcmVuY2UsIGFuZCBzbyBvbi4KICAKICBTaW1pbGFybHksICpuZWdhdGl2ZSBsb2cyLWZvbGQqIHZhbHVlcyBpbmRpY2F0ZSBhIGNoYW5nZSBpbiBnZW5lIGNvdW50cyBpbiB0aGUKICBvdGhlciBkaXJlY3Rpb246IHRoZSByZWZlcmVuY2UgbGV2ZWwgaXMgKmhpZ2hlciogdGhhbiB0aGUgb3RoZXIgbGV2ZWwuCiAgCi0gVGhlIGBsZmNTRWAgY29sdW1uIGluZGljYXRlcyB0aGUgdW5jZXJ0YWludHkgaW4gdGVybXMgb2YgdGhlIHN0YW5kYXJkIGVycm9yCiAgKFNFKSBvZiB0aGUgbG9nMi1mb2xkIGNoYW5nZSBlc3RpbWF0ZS4KICAKLSBUaGUgKipgc3RhdGAqKiBjb2x1bW4gaW5kaWNhdGVzIHRoZSB2YWx1ZSBmb3IgdGhlIFdhbGQgdGVzdCdzIHRlc3Qgc3RhdGlzdGljLgoKLSBUaGUgKipgcHZhbHVlYCoqIGNvbHVtbiByZXBvcnRlZCB0aGUgKnVuY29ycmVjdGVkKiBwLXZhbHVlIGZyb20gdGhlIFdhbGQgdGVzdC4KCi0gQmVjYXVzZSB3ZSBhcmUgdGVzdGluZyBzaWduaWZpY2FuY2UgZm9yICptYW55KiBnZW5lcywKICB3ZSBuZWVkIHRvIGNvcnJlY3QgZm9yIG11bHRpcGxlIHRlc3RpbmcuCiAgREVTZXEyIHVzZXMgdGhlIEJlbmphbWluaS1Ib2NoYmVyZyBGYWxzZSBEaXNjb3ZlcnkgUmF0ZSAoRkRSKSBjb3JyZWN0aW9uLAogIGFuZCB0aGVzZSB2YWx1ZXMgYXJlIHJlcG9ydGVkIGluIHRoZSBjb2x1bW4gKipgcGFkamAqKiAoaS5lLiwgYWRqdXN0ZWQgcC12YWx1ZSkuCgpBIHN1bW1hcnkgb2YgdGhpcyBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIGNvbHVtbiBjYW4gYmUgc2VlbiBieSBydW5uaW5nIHRoZQpgbWNvbHMoKWAgZnVuY3Rpb246CgpgYGB7cn0KbWNvbHMocmVzKQpgYGAKCiMjIyBgTkFgIHZhbHVlcyBpbiB0aGUgcmVzdWx0cyB0YWJsZQoKU29tZSB2YWx1ZXMgaW4gdGhlIHJlc3VsdHMgdGFibGUgY2FuIGJlIHNldCB0byBgTkFgIGZvciBvbmUgb2YgdGhlIGZvbGxvd2luZyByZWFzb25zOgoKLSBJZiBhIGdlbmUgY29udGFpbnMgYSBzYW1wbGUgd2l0aCBhIGNvdW50ICoqb3V0bGllcioqLAogIGJvdGggdGhlIHAtdmFsdWUgYW5kIGFkanVzdGVkIHAtdmFsdWUgd2lsbCBiZSBzZXQgdG8gYE5BYC4KICAoREVTZXEyIHBlcmZvcm1zIG91dGxpZXIgZGV0ZWN0aW9uIHVzaW5nIENvb2sncyBkaXN0YW5jZS4pCiAgCi0gSWYgYWxsIHNhbXBsZXMgaGF2ZSAqKnplcm8gY291bnRzKiogZm9yIGEgZ2l2ZW4gZ2VuZSwKICB0aGUgYGJhc2VNZWFuYCBjb2x1bW4gd2lsbCBiZSB6ZXJvLAogIGFuZCB0aGUgbG9nMi1mb2xkIGNoYW5nZSBlc3RpbWF0ZXMsCiAgcC12YWx1ZSBhbmQgYWRqdXN0ZWQgcC12YWx1ZSB3aWxsIGFsbCBiZSBzZXQgdG8gYE5BYC4KCi0gREVTZXEyIGFsc28gYXV0b21hdGljYWxseSBmaWx0ZXJzIGdlbmVzIHdpdGggYSAqKmxvdyBtZWFuIGNvdW50KioKICBpbiB0aGUgc2Vuc2UgdGhhdCBpdCBkb2VzIG5vdCBpbmNsdWRlIHRoZW0gaW4gdGhlIG11bHRpcGxlIHRlc3RpbmcgY29ycmVjdGlvbi4KICBUaGVyZWZvcmUsIGluIHN1Y2ggY2FzZXMsIHRoZSBwLXZhbHVlIHdpbGwgbm90IGJlIGBOQWAsCiAgYnV0IHRoZSAqYWRqdXN0ZWQqIHAtdmFsdWUgd2lsbCBiZS4KICAKICBCZWNhdXNlIHdlIGhhdmUgdmVyeSBsb3cgcG93ZXIgdG8gZGV0ZWN0IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGZvciBzdWNoCiAgbG93LWNvdW50IGdlbmVzLCBpdCBpcyBiZW5lZmljaWFsIHRvIHJlbW92ZSB0aGVtIHByaW9yIHRvIHRoZSBtdWx0aXBsZSB0ZXN0aW5nCiAgY29ycmVjdGlvbjogdGhhdCB3YXksIHRoZSBjb3JyZWN0aW9uIGJlY29tZXMgbGVzcyBzZXZlcmUgZm9yIHRoZSByZW1haW5pbmcgZ2VuZXMuCgpMZXQncyBzZWUgaG93IG1hbnkgZ2VuZXMgaGF2ZSBgTkFgIHAtdmFsdWVzOgoKYGBge3J9CiMgTnVtYmVyIG9mIGdlbmVzIHdpdGggTkEgcC12YWx1ZToKc3VtKGlzLm5hKHJlcyRwdmFsdWUpKQoKIyBBcyBhIHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgdGVzdDoKc3VtKGlzLm5hKHJlcyRwdmFsdWUpKSAvIG5yb3cocmVzKQpgYGAKCkFuZCBgTkFgIGFkanVzdGVkIHAtdmFsdWVzOgoKYGBge3J9CiMgTnVtYmVyIG9mIGdlbmVzIHdpdGggTkEgcC12YWx1ZToKc3VtKGlzLm5hKHJlcyRwYWRqKSkKCiMgQXMgYSBwcm9wb3J0aW9uIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIHRlc3Q6CnN1bShpcy5uYShyZXMkcGFkaikpIC8gbnJvdyhyZXMpCmBgYAoKPGJyPgoKLS0tLQoKIyMgREUgYW5hbHlzaXMgLS0gY29udHJhc3QgdHdvIGN1c3RvbSBncm91cHMKClVzaW5nIHRoZSBgcmVzdWx0c05hbWVzYCBmdW5jdGlvbiwKd2UgY2FuIHNlZSB3aGljaCBwYWlyd2lzZSBjb250cmFzdHMgYmV0d2VlbiBkaWZmZXJlbnQgbGV2ZWxzIG9mIHRoZSBmYWN0b3IgYXJlCmF2YWlsYWJsZSAodGhvdWdoIGl0IGlzIG5vdCBkaXNwbGF5ZWQgaW4gYSBwYXJ0aWN1bGFybHkgcmVhZGFibGUgZmFzaGlvbik6CgpgYGB7cn0KcmVzdWx0c05hbWVzKGRkcykKYGBgCgpOb3QgYWxsIHBhaXJ3aXNlIGNvbnRyYXN0cyBiZXR3ZWVuIHRoZSA1IGxldmVscyBpbiBvdXIgYGdyb3VwYCBmYWN0b3IgYXJlIGF2YWlsYWJsZQpoZXJlOiBpbnN0ZWFkLCBgY29udHJvbF9tb2NrYCwgd2hpY2ggd2Ugc2V0IGFzIHRoZSByZWZlcmVuY2UgbGV2ZWwsIGlzIGJlaW5nIGNvbXBhcmVkCndpdGggYWxsIDQgb3RoZXIgbGV2ZWxzLiAoSG93ZXZlciwgd2UgY2FuIHN0aWxsIGNvbXBhcmUgb3RoZXIgbGV2ZWxzIHRvby4pCgpBYm92ZSwgd2UgbG9va2VkIGF0IHRoZSByZXN1bHRzIChwLXZhbHVlcyBhbmQgc28gb24pIGZvciB0aGUgbGFzdCBvZiB0aGVzZQpjb21wYXJpc29ucywgd2hpY2ggaXMgd2hhdCBERVNlcTIgd2lsbCBzaG93IGJ5IGRlZmF1bHQuClRvIHNlZSB0aGUgcmVzdWx0cyB0YWJsZSBmb3Igb25lIG9mIHRoZSBvdGhlciAzIGNvbXBhcmlzb25zLAp3ZSBwYXNzIGEgdmVjdG9yIHRvIHRoZSBgY29udHJhc3RgIGFyZ3VtZW50IG9mIHRoZSBgcmVzdWx0cygpYCBmdW5jdGlvbiB3aXRoCnRoZSBmYWN0b3IgKGBncm91cGApIGFuZCB0aGUgdHdvIGxldmVscyB0byBiZSBjb250cmFzdGVkOgoKYGBge3J9Cm15X2NvbnRyYXN0IDwtIGMoIlJpX21vY2siLCAiY29udHJvbF9tb2NrIikKCnJlcyA8LSByZXN1bHRzKGRkcywKICAgICAgICAgICAgICAgY29udHJhc3QgPSBjKCJncm91cCIsIG15X2NvbnRyYXN0KSkKYGBgCgpIb3cgbWFueSBhZGp1c3RlZCBwLXZhbHVlcyB3ZXJlIGxlc3MgdGhhbiAwLjE/CgpgYGB7cn0Kc3VtKHJlcyRwYWRqIDwgMC4xLCBuYS5ybSA9IFRSVUUpCmBgYAoKV2UnbGwgYWxzbyBjcmVhdGUgYW4gb2JqZWN0IHdpdGggYWRqdXN0ZWQgKHNocnVua2VuKSBMRkMgZXN0aW1hdGVzLAp3aGljaCB3aWxsIGJlIHVzZWZ1bCBmb3IgdmlzdWFsaXphdGlvbiBhbmQgcmFua2luZyBvZiBnZW5lcyBieSBMRkM6CgpgYGB7cn0KbXlfY29lZiA8LSBwYXN0ZTAoImdyb3VwXyIsIHBhc3RlMChteV9jb250cmFzdCwgY29sbGFwc2UgPSAiX3ZzXyIpKQpteV9jb2VmCnJlc19MRkMgPC0gbGZjU2hyaW5rKGRkcywgY29lZiA9IG15X2NvZWYsIHR5cGUgPSAiYXBlZ2xtIiwgbGZjVGhyZXNob2xkID0gMSkKYGBgCgpOb3RlIHRoYXQgaGVyZSwgd2UgaGFkIHRvIHByb3ZpZGUgdGhlIGNvbnRyYXN0ICgiY29lZmZpY2llbnQiKSBpbiB0aGUgZm9ybWF0CmdpdmVuIGJ5IGByZXN1bHRzTmFtZXMoZGRzKWAKKHRoYXQgbG9va3MgYSBiaXQgY29uZnVzaW5nIGJlY2F1c2UgdGhpcyBmb3JtYXQgdXNlcyB1bmRlcnNjb3JlcyB0byBzZXBhcmF0ZSBsZXZlbHMsCndoaWxlIG91ciBmYWN0b3IgbGV2ZWxzIHRoZW1zZWx2ZXMgYWxzbyBjb250YWluIHVuZGVyc2NvcmVzKS4KCjxicj4KCi0tLS0KCiMjIEV4cGxvcmluZyB0aGUgcmVzdWx0cwoKIyMjIE1BLXBsb3Q6CgpGb3IgYSBuaWNlIG92ZXJ2aWV3IG9mIHRoZSByZXN1bHRzLCB3ZSBjYW4gcGxvdCBhIHNvLWNhbGxlZCAiTUEgcGxvdCIuCkFuIE1BIHBsb3Qgc2hvd3MsIGZvciBlYWNoIGdlbmU6Ci0gQ291bnQgZGlmZmVyZW5jZXMgaW4gdGVybXMgb2YgTEZDIGJldHdlZW4gdHdvIGdyb3Vwcywgb24gdGhlIHktYXhpcwotIE1lYW4gY291bnRzIGFjcm9zcyBib3RoIGdyb3Vwcywgb24gdGhlIHgtYXhpcy4KCldlIGNhbiBjcmVhdGUgYW4gTUEgcGxvdCB1c2luZyBERVNlcTIncyBgcGxvdE1BYCBmdW5jdGlvbiwKd2l0aCBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBkaXNwbGF5ZWQgaW4gYmx1ZToKCmBgYHtyfQpwbG90TUEocmVzLCB5bGltID0gYygtNSwgNSkpCmBgYAoKVG8gYmUgYWJsZSB0byBjdXN0b21pemUgdGhlIHBsb3QsCndlJ2xsIHVzZSBgcmV0dXJuRGF0YSA9IFRSVUVgIGxpa2Ugd2UgaGF2ZSBkb25lIHdpdGggcHJldmlvdXMgcGxvdHMsCmFuZCB0aGVuIHBsb3QgdGhlIHJlc3VsdGluZyBkYXRhZnJhbWUgd2l0aCAqZ2dwbG90Mio6CgpgYGB7cn0KZCA8LSBwbG90TUEocmVzLCByZXR1cm5EYXRhID0gVFJVRSkKCmdncGxvdChkLCBhZXMoeCA9IG1lYW4sIHkgPSBsZmMsIGNvbG9yID0gaXNERSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xMCwgMTApKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2dyZXk1MCcsICdibHVlJykpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJNZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIiwKICAgICAgIHkgPSAiTEZDIikKYGBgCgpXZSBjYW4gc2VlIHRoYXQgbG93bHktZXhwcmVzc2VkIGdlbmVzIHRlbmQgdG8gZGV2aWF0ZSBmcm9tIGFuIExGQyBvZiAwCihzYW1lIG1lYW4gZXhwcmVzc2lvbiBsZXZlbHMgaW4gdGhlIHR3byBncm91cHMpIG11Y2ggbW9yZSB0aGFuCmhpZ2hseS1leHByZXNzZWQgZ2VuZXMgZG8uCkhvd2V2ZXIsIHRoaXMgaXMgYW4gYXJ0aWZhY3Qgb2Ygbm9pc2Ugb3ZlcnBvd2VyaW5nIHRoZSBzaWduYWwgd2hlbgpleHByZXNzaW9uIHZhbHVlcyBhcmUgbG93LgpXZSBjYW4gYWxzbyBzZWUgdGhhdCBubyBnZW5lcyBpbiB0aGUgZmFyIGxlZnQgcGFydCBvZiB0aGUgcGxvdCBhcmUKZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkOiB0aGlzIGlzIGR1ZSB0byB0aGlzIHNhbWUgbGFjayBvZiBwb3dlci4KCkRFU2VxMiBwcm92aWRlcyBzZXZlcmFsIG1ldGhvZHMgdG8gYWRqdXN0IExGQyBlc3RpbWF0ZXMgZm9yIHRoaXMgbG93LWV4cHJlc3Npb24KYmlhcy4KV2UgdXNlZCBvbmUgb2YgdGhvc2UgKGBsZmNTaHJpbmsoKWApIGFib3ZlIHRvIHByb2R1Y2UgdGhlIGByZXNfTEZDYCBvYmplY3QuCkxldCdzIGNyZWF0ZSBhbm90aGVyIE1BIHBsb3Qgd2l0aCB0aGVzZSBhZGp1c3RlZCBMRkMgZXN0aW1hdGVzOgoKYGBge3J9CmQgPC0gcGxvdE1BKHJlc19MRkMsIHlsaW0gPSBjKC01LCA1KSwgcmV0dXJuRGF0YSA9IFRSVUUpCgpnZ3Bsb3QoZCwgYWVzKHggPSBtZWFuLCB5ID0gbGZjLCBjb2xvciA9IGlzREUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41KSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnZ3JleTUwJywgJ2JsdWUnKSkgKwogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArCiAgbGFicyh4ID0gIk1lYW4gb2Ygbm9ybWFsaXplZCBjb3VudHMiLAogICAgICAgeSA9ICJTaHJ1bmtlbiBMRkMiKQpgYGAKCkZpbmFsbHksIGZvciBhIHBsb3QgbGlrZSB0aGlzIGl0IGNvdWxkIGJlIHVzZWZ1bCB0byBiZSBhYmxlIHRvIGlkZW50aWZ5IGluZGl2aWR1YWwKZ2VuZXMuIFRoZXJlIGFyZSB3YXkgdG9vIG1hbnkgdG8gcHJpbnQgdGhlIGdlbmUgbmFtZXMgb24gdGhlIHBsb3QsIHRob3VnaC4KSW5zdGVhZCwgd2UgY2FuIGFsc28gbWFrZSB0aGUgcGxvdCAqKmludGVyYWN0aXZlIHdpdGggUGxvdGx5KiosCnNvIHdlIGNhbiBzZWUgdGhlIGlkZW50aXR5IG9mIGVhY2ggZ2VuZSAqd2hlbiB3ZSBob3ZlciBvdmVyIHRoZSBwb2ludCo6CgpgYGB7cn0KbGlicmFyeShwbG90bHkpCgojIFRvIHNob3cgdGhlIGdlbmUgbmFtZSwgd2UgbmVlZCB0byBoYXZlIGEgY29sdW1uIHdpdGggZ2VuZSBuYW1lcy4KIyBDdXJyZW50bHksIHRoZSBnZW5lIG5hbWVzIGFyZSByb3cgbmFtZXMgYnV0IGdncGxvdDIgKGFuZCBvdGhlciB0aWR5dmVyc2UKIyBhcHBsaWNhdGlvbnMpIGRvbid0IGxpa2UgdGhhdCwgc28gd2UgY3JlYXRlIGEgY29sdW1uIHdpdGggZ2VuZSBuYW1lczoKZCRnZW5lIDwtIHJvd25hbWVzKGQpCgojIEZpcnN0IHdlIGNyZWF0ZSBhIHZlcnkgc2ltaWxhciBnZ3Bsb3QgdG8gd2hhdCB3ZSBkaWQgYWJvdmUsCiMgYnV0IHdlIGFzc2lnbiBnZW5lIG5hbWVzIHRvICJ0ZXh0IjoKcF9tYSA8LSBnZ3Bsb3QoZCwKICAgICAgICAgICAgICAgYWVzKHggPSBtZWFuLCB5ID0gbGZjLCBjb2xvciA9IGlzREUsIHRleHQgPSBnZW5lKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDAuNSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2dyZXk1MCcsICdibHVlJykpICsKICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJNZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIiwKICAgICAgIHkgPSAiU2hydW5rZW4gTEZDIikKCiMgRmluYWxseSwgd2UgbWFrZSB0aGUgcGxvdCBpbnRlcmFjdGl2ZSBhbmQgdGVsbCBQbG90bHkgdGhhdCBpdCBzaG91bGQgc2hvdwojIHRoZSAidGV4dCIgKGkuZS4gZ2VuZSBuYW1lcykgYXMgdGhlICJ0b29sdGlwIiwgbWVhbmluZyB1cG9uIGhvdmVyaW5nOgpnZ3Bsb3RseShwX21hLCB0b29sdGlwID0gInRleHQiKQpgYGAKCiMjIyBQbG90IHNwZWNpZmljIGdlbmVzCgpXZSBjYW4gYWxzbyBjcmVhdGUgcGxvdCBvZiBleHByZXNzaW9uIGxldmVscyBmb3IgaW5kaXZpZHVhbCBnZW5lcy4KVGhhdCBpcyBlc3BlY2lhbGx5IGludGVyZXN0aW5nIGZvciBnZW5lcyB3aXRoIGhpZ2hseSBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwKZXhwcmVzc2lvbi4KCkxldCdzIHBsb3QgdGhlIHRvcC01IG1vc3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXM6CgpgYGB7ciwgZXZhbD1UUlVFfQojIEZpcnN0LCB3ZSBzZWxlY3QgdGhlIDUgZ2VuZXMgd2l0aCB0aGUgbG93ZXN0IGFkanVzdGVkIHAtdmFsdWU6CnRvcDUgPC0gcm93Lm5hbWVzKHJlc1tvcmRlcihyZXMkcGFkaiksIF1bMTo1LCBdKQoKIyBUaGVuIHdlIGxvb3Agb3ZlciB0aGVzZSBnZW5lcywgY3JlYXRpbmcgYSBwbG90IGZvciBlYWNoIG9uZToKZm9yKGZnZW5lIGluIHRvcDUpIHsKICAKICBkIDwtIHBsb3RDb3VudHMoZGRzLAogICAgICAgICAgICAgICAgICBnZW5lID0gZmdlbmUsCiAgICAgICAgICAgICAgICAgIGludGdyb3VwID0gImdyb3VwIiwKICAgICAgICAgICAgICAgICAgcmV0dXJuRGF0YSA9IFRSVUUpCgogIHAgPC0gZ2dwbG90KGQsIGFlcyh4ID0gZ3JvdXAsIHkgPSBjb3VudCkpICsKICAgICAgICAgIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIodyA9IDAuMSwgaCA9IDApKSArCiAgICAgICAgICBsYWJzKHRpdGxlID0gZmdlbmUpICsKICAgICAgICAgIHRoZW1lX2J3KCkKICAKICBwcmludChwKQp9CgpgYGAKCiMjIyBIZWF0bWFwCgpXZSBjYW4gY3JlYXRlIGhlYXRtYXBzIHdpdGggdGhlIGBwaGVhdG1hcGAgZnVuY3Rpb24gLS0KZm9yIGluc3RhbmNlLCBmb3IgdGhlIDIwIG1vc3QgaGlnaGx5IGV4cHJlc3NlZCBnZW5lczoKCmBgYHtyLCBldmFsPVRSVUV9Cm50ZCA8LSBub3JtVHJhbnNmb3JtKGRkcykKc2VsZWN0aW9uIDwtIG9yZGVyKHJvd01lYW5zKGNvdW50cyhkZHMsIG5vcm1hbGl6ZWQgPSBUUlVFKSksCiAgICAgICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gVFJVRSlbMToyMF0KbnRkX3NlbCA8LSBhc3NheShudGQpW3NlbGVjdGlvbiwgXQpkZl9tZXRhIDwtIGFzLmRhdGEuZnJhbWUoY29sRGF0YShkZHMpWywgYygiQU1GIiwgIlRyZWF0bWVudCIpXSkKYGBgCgpgYGB7ciwgZXZhbD1UUlVFfQpwaGVhdG1hcChudGRfc2VsLAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBkZl9tZXRhKQpgYGAKCiMjIyBFeHBvcnQgdGhlIHJlc3VsdHMKCkxldCdzIHNhdmUgdGhlIHJlc3VsdHMgZGF0YWZyYW1lIHRvIGZpbGUuCgpOb3RlIHRoYXQgaXQgd2lsbCBvbmx5IGNvbnRhaW4gdGhlIHJlc3VsdHMgZm9yIG9uZSBjb21wYXJpc29uLgpBbHNvLCBpZiB3ZSB3cml0ZSB0aGUgcmVzdWx0cyBkYXRhZnJhbWUgdG8gZmlsZSwKd2Ugd29uJ3QgYmUgYWJsZSB0byB0ZWxsIGZyb20gdGhlIGZpbGUgd2hhdCB0aGUgY29tcGFyaXNvbiBpcywgc28gbGV0J3Mgc3RvcmUKdGhhdCBpbiB0d28gY29sdW1uczoKCmBgYHtyfQpteV9jb250cmFzdAoKbXlfY29udHJhc3RfcGFzdGVkIDwtIHBhc3RlMChteV9jb250cmFzdCwgY29sbGFwc2UgPSAiX3ZzXyIpCm15X2NvbnRyYXN0X3Bhc3RlZApgYGAKCmBgYHtyfQpyZXMkbGV2ZWwxIDwtIG15X2NvbnRyYXN0WzFdCnJlcyRsZXZlbDIgPC0gbXlfY29udHJhc3RbMl0KaGVhZChyZXMpCmBgYAoKTm93IHdlIGNhbiB3cml0ZSBgcmVzYCB0byBmaWxlOgoKYGBge3IsIGV2YWw9RkFMU0V9CnJlc19maWxlIDwtIGZpbGUucGF0aChvdXRkaXIsIHBhc3RlMChteV9jb250cmFzdF9wYXN0ZWQsICdfYWxsLXJlcy50eHQnKSkKCndyaXRlLnRhYmxlKHJlcywgcmVzX2ZpbGUsCiAgICAgICAgICAgIHNlcCA9ICdcdCcsIHJvdy5uYW1lcyA9IFRSVUUsIHF1b3RlID0gRkFMU0UpCmBgYAoKV2UgbWF5IGFsc28gd2FudCB0byBzYXZlIGEgc2VwYXJhdGUgdGFibGUgd2l0aCBvbmx5IHNpZ25pZmljYW50IHJlc3VsdHM6CgpgYGB7ciwgZXZhbD1GQUxTRX0KcmVzX3NpZ19maWxlIDwtIGZpbGUucGF0aChvdXRkaXIsIHBhc3RlMChteV9jb250cmFzdF9wYXN0ZWQsICdfc2lnLXJlcy50eHQnKSkKCnJlc19zaWcgPC0gcmVzW3JlcyRwYWRqIDwgMC4xLCBdCgp3cml0ZS50YWJsZShyZXNfc2lnLCByZXNfc2lnX2ZpbGUsCiAgICAgICAgICAgIHNlcCA9ICdcdCcsIHJvdy5uYW1lcyA9IFRSVUUsIHF1b3RlID0gRkFMU0UpCmBgYAoKPGJyPgoKLS0tLQoKIyMgREUgYW5hbHlzaXMgLS0gYWxsIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpUQkEKCi0tLS0KCiMjIERFIGFuYWx5c2lzIC0tIHdpdGggdHdvIGZhY3RvcnMKClRCQQoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmRkc18yZl9yYXcgPC0gZGRzX3JhdwoKIyBBTUYgKyBUcmVhdG1lbnQ6IFRlc3QgZm9yIHRoZSBlZmZlY3Qgb2YgVHJlYXRtZW50ICh0aGUgbGFzdCBmYWN0b3IpLAojIC4uLmNvbnRyb2xsaW5nIGZvciB0aGUgZWZmZWN0IG9mIEFNRiAodGhlIGZpcnN0IGZhY3RvcikuCmRlc2lnbihkZHNfMmZfcmF3KSA8LSBmb3JtdWxhKH4gQU1GICsgVHJlYXRtZW50KQoKIyBPciB1c2UgaW50ZXJhY3Rpb24gdGVybTog4oi8IEFNRiArIFRyZWF0bWVudCArIEFNRjpUcmVhdG1lbnQKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KIyBSdW4gREVTZXEgd2l0aCB0aGUgbmV3IGRlc2lnbjoKZGRzXzJmIDwtIERFU2VxKGRkc18yZl9yYXcpCmBgYAoK